﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;

namespace com.susi.util {
    /// <summary>
    ///     指示这个函数为对象加载器，需要确保该函数为static ins (string)，由<see cref="ReflectionUtil" />使用
    /// </summary>
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)]
    public class InstanceLoaderByString : Attribute {
        [CanBeNull]
        public static MethodInfo GetLoaderMethod([NotNull] Type type)
        {
            var loader =
                type.GetMethods()
                    .Where(v =>
                           {
                               if (!v.IsStatic) return false;
                               var atr = v.GetCustomAttribute<InstanceLoaderByString>();
                               if (atr == null) return false;
                               if (!type.IsAssignableFrom(v.ReturnType)) return false;
                               var paras = v.GetParameters();
                               return paras.Length == 1 && paras[0].ParameterType == typeof(string);
                           })
                    .FirstOrDefault();
            return loader;
        }

        [CanBeNull]
        public static ConstructorInfo GetLoaderConstructor([NotNull] Type type)
        {
            var loader = type.GetConstructors()
                             .Where(v =>
                                    {
                                        var atr = v.GetCustomAttribute<InstanceLoaderByString>();
                                        if (atr == null) return false;
                                        var paras = v.GetParameters();
                                        return paras.Length == 1 && paras[0].ParameterType == typeof(string);
                                    })
                             .FirstOrDefault();
            return loader;
        }

        /// <summary>
        ///     返回null表示该对象不支持InstanceLoaderByString方式创建
        /// </summary>
        [CanBeNull]
        public static Func<string, object> CreatLoader([NotNull] Type type)
        {
            if (type == typeof(string)) return v => v;

            if (type == typeof(float)) return str => float.Parse(str);

            if (type == typeof(int)) return str => int.Parse(str);

            if (type == typeof(bool)) return str => bool.Parse(str);

            if (type == typeof(Type)) {
                object __localLoad(string str)
                {
                    var asbs     = AppDomain.CurrentDomain.GetAssemblies();
                    var foundres = new List<Type>();
                    foreach (var asb in asbs) {
                        var typ = asb.GetType(str);
                        if (typ != null) foundres.Add(typ);
                    }

                    if (foundres.Count > 1) {
#if USING_LITJSON
                        var json = foundres.Select(v => $"{v.FullName} in {v.Assembly.FullName}").ToArray().SerializeToPrettyCHJson();
                        throw new Exception($"[error]存在多个同名类型\n{json}");
#endif
                        throw new Exception($"[error]存在多个同名类型\n");
                    }

                    return foundres.Count == 0 ? null : foundres[0];
                }

                return __localLoad;
            }

            if (type.IsEnum) {
                //按CHKEY搜索值
                // Debug.Log(type.FullName+str);
                object __localLoadEnum(string str)
                {
                    var chkeySearchRes = type.GetFields().Where(v =>
                                                                {
                                                                    var desc = v.GetCustomAttribute<DescriptionAttribute>();
                                                                    if (desc == null) return false;
                                                                    return desc.Description == str;
                                                                }).ToArray();
                    if (chkeySearchRes.IsMoreThan(1)) throw new Exception($"[error][{type.FullName}:{str}]CHKEY不唯一");
                    var fieldCHKEY = chkeySearchRes.FirstOrDefault();
                    return Enum.Parse(type, fieldCHKEY != null ? fieldCHKEY.Name : str); //CHKEY没有就当ENKEY处理
                }

                return __localLoadEnum;
            }

            var loader = GetLoaderMethod(type);
            if (loader != null) return str => loader.Invoke(null, new object[] {str});

            var ctor = GetLoaderConstructor(type);
            if (ctor != null) return str => ctor.Invoke(new object[] {str});

            return null;
        }
    }
}